#include "Type.h"

#include "BasicTypeOperator.h"
#include "ClassMeta.h"

namespace MOE
{
    namespace Core
    {
		// Defines a default instance.
		const Type Type::kDefault = Type();

		// Default constructor for Type.
		// This constructs a Nil type.
		Type::Type()
			: mBaseClassMeta(ClassMeta::kNil)
			, mReferenceType(ReferenceType::kValue)
			, mArrayType(ArrayType::kSingle)
			, mNumElements(1)
		{
			mTypeSize = mBaseClassMeta.GetSize();
		}

		// Constructor for Type.
		// Parameters:
		// - baseClassMeta: Pointer to the base ClassMeta used by this type.
		// - refType: Enum to determine whether this is a value or a reference. In an array, this applies to each element of the array.
		// - arrType: Enum to determine whether this is a single value or an array.
		// - numElements: Number of elements in the array. Only used by ArrayType::kEmbeddedArray, otherwise this value is ignored and GetNumElements will always return 1.
		Type::Type(const ClassMeta& baseClassMeta, ReferenceType refType, ArrayType arrType, tSize numElements)
			: mBaseClassMeta(baseClassMeta)
			, mReferenceType(refType)
			, mArrayType(arrType)
			, mNumElements(numElements)
		{
			// Number of elements will always be 1 for single.
			if (mArrayType != ArrayType::kEmbeddedArray)
			{
				mNumElements = 1;
			}

			// Calculate total size of this type.
			if (mArrayType != ArrayType::kRemoteArray)
			{
				tSize elementSize = 0;
				switch (mReferenceType)
				{
				case ReferenceType::kValue:
					elementSize = mBaseClassMeta.GetSize();
					break;
				case ReferenceType::kReference:
					elementSize = sizeof(tUntypedRef);
					break;
				}
				mTypeSize = elementSize * mNumElements;
			}
			else
			{
				// For remote array, the field only stores a reference to the array.
				mTypeSize = sizeof(tUntypedRef);
			}
		}

		// Copy the value contained in this type from source to destination.
		// Reference types only copy the reference (shallow copy).
		void Type::CopyValue(tUntypedConstRef source, tUntypedRef destination) const
		{
			switch (mArrayType)
			{
			case ArrayType::kSingle:
				switch (mReferenceType)
				{
				case ReferenceType::kValue:
					mBaseClassMeta.CopyValue(source, destination);
					break;
				case ReferenceType::kReference:
					BasicTypeOperator::ReferenceOperator::CopyData(source, destination);
					break;
				}
				break;
			case ArrayType::kEmbeddedArray:
				switch (mReferenceType)
				{
				case ReferenceType::kValue:
					mBaseClassMeta.CopyArray(source, destination, mNumElements);
					break;
				case ReferenceType::kReference:
					BasicTypeOperator::ReferenceOperator::CopyArray(source, destination, mNumElements);
					break;
				}
				break;
			case ArrayType::kRemoteArray:
				// Same operation regardless of the array's content.
				BasicTypeOperator::ReferenceOperator::CopyData(source, destination);
				break;
			}
		}
    }
}
